home *** CD-ROM | disk | FTP | other *** search
- /* MGRAPH.C - Library of graphics functions for machines whose BIOS and
- * display adapter hardware are compatible with that of the IBM PC.
- * These routines are for use with the Microsoft C compiler version 4.0.
- * All routines in this file were written by Paul Ketrick, and all
- * comments and suggestions regarding these routines are welcome;
- * please leave me a message on either Mike's C BBS at (619)722-8724
- * or the Programmer's Forum at (818)701-1021. Feel free to use this
- * library in any way you like, but please give me credit for it in
- * your source code.
- *
- * Functions in this library:
- * setvid() - Sets the video display mode. Modes 0-7 as defined by IBM
- * are supported through the BIOS. This function also supports
- * modes 8 and 9 (as defined by me) which are 160x100 16-color
- * low-res graphics modes with colorburst output disabled and
- * enabled, respectively.
- * pset() - Plots a point on the screen at the given x-y coordinates
- * with the given color. Supports standard graphics modes 4-6
- * and also modes 8 and 9.
- * line() - Draws a line on the screen from one point to another.
- * Supports all graphics modes supported by the pset() routine.
- * fline() - Fast line-drawing routine. Only graphics modes 4-6 (the
- * 320x200 and 640x200 modes) are currently supported by this
- * function.
- * box() - Draws the outline of a box on the screen. Supports all
- * graphics modes supported by the pset() routine.
- * fbox() - Fast box-drawing routine. Graphics modes 4-6 only are
- * supported.
- * fillbox() - Draws a filled-in box on the screen. Supports all graphics
- * modes supported by the pset() routine.
- * ffillbox() - Fast filled-in box-drawing routine. Graphics modes 4-6
- * only are supported.
- * circle() - Draws a circle on the screen. Supports all graphics modes
- * supported by the pset() routine.
- * fcircle() - This is the fastest (and also probably the most compact)
- * of the three circle-drawing functions included in this
- * library; it works with all graphics modes supported
- * by the pset() routine.
- * fellipse() - Fast ellipse-drawing routine used by the fcircle() routine
- * described above; works with all graphics modes supported
- * by the pset() function.
- * cls() - Clears the screen. Supports all graphics modes supported
- * by the setvid() routine.
- *
- *
- * These routines are for use with the Microsoft C Compiler Version 4.0
- * and the Microsoft Macro Assembler Version 4.0; they will probably
- * work on older versions of the compiler and/or the assembler, but I
- * have not experimented much. The box(), fbox(), fillbox(), ffillbox(),
- * circle(), pcircle(), fcircle() and cls() functions are written entirely
- * in C; the setvid() function is a combination of C and assembly language;
- * and the pset(), line() and fline() routines are entirely in assembly
- * language. I have not found that any special switches or options need
- * to be used when compiling and assembling this code. LINK version
- * 3.51 (supplied with the C compiler) should be used to link programs
- * with this library.
- *
- * Function usage:
- *
- * void setvid(mode)
- * Sets the current video mode. Initialization of modes 0 through 7 is
- * performed entirely by the BIOS. Modes 8 and 9 are handled partially
- * through the BIOS and partially through assembly language code in
- * function init160(). The assembly language functions setpjmp() and
- * doint() are also used in the initialization of all graphics modes.
- *
- * void pset(x, y, color)
- * Plots a point with the specified color at coordinates (x,y) on the
- * screen; point (0,0) is at the top left corner of the screen. All
- * 640x200, 320x200 and 160x100 graphics modes are supported.
- * No boundary checking is performed on the coordinates passed
- * to this routine; therefore points whose coordinates lie outside the
- * screen boundaries either will not be displayed or will be displayed
- * at incorrect locations on the screen. In the 320x200 modes the
- * color may be 0 to 3; in the 640x200 mode it may be 0 to 1; and
- * in the 160x100 modes it can range from 0 to 15. Only the
- * relevant bits of the color are used, however, so no masking off
- * of the color value need be done before passing it to this function.
- *
- * void line(x1, y1, x2, y2, color)
- * Draws a line on the screen in the specified color from coordinates
- * (x1,y1) to (x2,y2). Although this routine draws lines with average
- * speed it will work with whatever graphics modes are supported by the
- * pset() routine and should be good for display adapters up to
- * 65536x65536 resolution.
- *
- * void fline(x1, y1, x2, y2, color)
- * Draws a line on the screen in the specified color from coordinates
- * (x1,y1) to (x2,y2). This routine only works with the 320x200 and
- * 640x200 modes, but is faster than anything I've seen. It
- * averages around 100 vectors per second in the 640x200 mode on my
- * 4.77 MHz PC with a NEC V20 processor installed.
- *
- * void box(x1, y1, x2, y2, color)
- * Draws the outline of a rectangle on the screen, two of
- * whose corners are at coordinates (x1,y1) and (x2,y2), in the
- * specified color. Like the line() routine, this routine supports
- * whatever graphics modes are supported by the pset() routine
- * which it calls.
- *
- * void fbox(x1, y1, x2, y2, color)
- * This routine is functionally the same as the box() routine but
- * uses the fline() function for much faster operation.
- *
- * void fillbox(x1, y1, x2, y2, color)
- * Draws a filled-in rectangle on the screen, two of whose corners
- * are at coordinates (x1,y1) and (x2,y2), in the specified color.
- * This routine is rather slow, but like the line() routine is
- * very general.
- *
- * void ffillbox(x1, y1, x2, y2, color)
- * This routine is functionally identical to the fillbox() routine
- * but uses the fline() function for much faster operation.
- *
- * void circle(x, y, radius, color)
- * Draws on the screen the outline of a circle with center coordinates
- * (x,y) and the given radius, in the specified color. This routine
- * is acceptably fast on 8087 systems but can be agonizingly slow on
- * on machines without a math coprocessor. It works with all graphics
- * modes supported by the pset() routine.
- *
- * void fcircle(x, y, radius, color)
- * This is a very fast circle-drawing routine. Its parameters are the
- * same as those of the circle() function above. It works with all
- * graphics modes supported by the pset() function.
- *
- * void fellipse(x, y, a, b, color)
- * This very fast ellipse-drawing function actually gets called by the
- * fcircle() routine described above. It draws an ellipse centered at
- * coordinates (x,y) with width 2*a and height 2*b in the specified
- * color. It works with all graphics modes supported by the pset()
- * function.
- *
- * void cls()
- * This routine has no parameters and simply calls the setvid()
- * routine without changing the current video mode, effectively
- * clearing the screeen.
- */
-
- #include <stdio.h>
- #include <math.h> /* Contains declarations for math
- * functions.
- */
-
- #define PI 3.14159265359
- #define SWAP(x, y) {x-=y; y+=x; x=y-x;} /* Swaps two integer variables without
- * using a temporary variable.
- */
-
-
- /* E X T E R N A L V A R I A B L E S
- *
- * Note: The following global variables are all explicitly initialized so
- * they will not be placed in a "FAR_BSS" segment by the compiler when the
- * large-data memory models are used. Rather, they will be placed in the
- * default (near) data segment so they can be accessed as near data by
- * the assembly language routines.
- */
-
- unsigned int vidmseg=0; /* Holds video memory base segment. */
- int mode=0; /* Current video mode. */
- double aspect=0.0; /* Ratio of width of a horizontal
- * dot to height of a vertical dot;
- * for my screen, 2.0 in the 640x200
- * mode, 1.0 for the 320x200 mode
- * and 1.0 in the 160x100 mode worked
- * best.
- */
- int bitspixl=0; /* Holds number of bits per pixel in
- * video memory.
- */
-
- /* Set the video display mode and initialize the video display hardware;
- * supports the following video modes through the BIOS:
- * 0=40x25 b/w text 1=40x25 color text
- * 2=80x25 b/w text 3=80x25 color text
- * 4=320x200 b/w graphics 5=320x200 color graphics
- * 6=640x200 b/w graphics
- * 7=80x25 mono text
- *
- * Graphics modes unsupported by BIOS ROM:
- * 8=160x100 16-color, colorburst disabled
- * 9=160x100 16-color, colorburst enabled
- */
-
- setvid(newmode)
- int newmode;
-
- {
- int i;
-
- mode = newmode;
-
- if (mode!=8 && mode!=9) {
- doint(16, (0 << 8)+mode); /* Call BIOS to initialize modes
- * 0 through 7.
- */
- if (mode == 7)
- vidmseg=0xb000;
-
- else {
- vidmseg=0xb800;
-
- if (mode == 4 || mode == 5) {
- aspect = 1.0; /* This may be changed to match a
- * particular monitor.
- */
- bitspixl = 2;
- } else if (mode == 6) {
- aspect = 2.0; /* Same here. */
- bitspixl = 1;
- }
- }
- } else { /* Must be a 160x100 mode. */
-
- /* The CGA's 160x100 16-color graphics mode requires some unconventional
- * use of the CRT controller. This "graphics mode" is actually programmed
- * as a text mode with a display 80 columns wide and 100 rows high, each
- * character being 8 dots wide and 2 dots high (640x200 dots total). Since
- * it is a text mode, 16 different colors can be displayed. The way 160-
- * column graphics are acheived is by storing character DD hex (221 decimal)
- * at every screen location. This graphics character appears as a solid
- * rectangle which fills up the left half of its 8x8 character space
- * (or, in this case, its 8x2 character space). During the time period
- * in the raster scan that the left half of this character is being
- * displayed, the character generator directs the foreground color of
- * the particular character location to be displayed, and when the right
- * half of the character is being displayed, the background color of the
- * character location is outputted. Thus, setting the foreground or
- * background color of a character location sets the color attribute of
- * a single 4-dot by 2-dot block on the screen. Display memory mapping
- * is an extension of the 80x25 text mode memory map: Even video memory
- * addresses contain characters and odd memory addresses contain attribute
- * bytes. The equation below converts x-y coordinates to video memory
- * offsets:
- * offset = y * 160 + (x >> 1) * 2
- * where x=0-159 and y=0-99. The only other special thing about this video
- * mode is that the "blinking attribute bit" must be disabled; doing this
- * enables the use of 16 background colors as well as 16 foreground colors.
- */
-
- doint(16, (0 << 8) + mode - 6); /* Call the BIOS to initialize the
- * 80x25 b/w or color text mode.
- */
- vidmseg = 0xb800;
- aspect = 1.0; /* This may be changed to match a
- * particular monitor.
- */
- bitspixl = 4;
- init160(); /* Now call the assembly language
- * routine to do the fancy stuff
- * with the CRTC (documentation
- * on this is in the file
- * msgraph.asm).
- */
- }
-
- setpjmp(mode); /* Call assembly language routine to
- * set up jump address for pset()
- * (see pset() source in msgraph.asm)
- */
- }
-
- /* D R A W A C I R C L E
- */
-
- /* The following two routines draw a circle centered at the specified
- * coordinates with the specified radius. Note that the circle's height
- * will be equal to "radius" pixels while its width will be equal to
- * (aspect * radius) pixels to compensate for the screen's aspect ratio.
- */
-
- /* The routine below draws circles using polar coordinates. It is
- * roughly 20 times slower than the circle routine which follows it and
- * is included only for the sheer heck of it.
- */
-
- void pcircle(x, y, radius, color)
- unsigned int x, y, radius, color;
-
- {
- float deg;
-
- for (deg=0.01; deg <= PI*2; deg+=PI/4/radius)
- pset((int)(radius * cos(deg) * aspect + x),
- (int)(radius * sin(deg) + y), color);
- }
-
- /* The following routine draws a circle on the screen with the given center
- * coordinates and radius. It runs pretty fast on systems with a math
- * coprocessor, but may be too slow on machines without an 8087. However,
- * it is probably about as fast as a floating-point-based circle-drawing
- * routine will get.
- */
-
- void circle(x, y, radius, color)
- unsigned int x, y, radius, color;
-
- {
- int a, b, dx, dy;
- int am = radius * sqrt(0.5) * aspect + 1;
- int bm = radius * sqrt(0.5) + 1;
- double aspect2 = aspect * aspect;
- double xs=radius * radius * aspect2;
- double ys=radius * radius;
-
- for (a=0; a < am; a++) {
- dy = sqrt(xs - a*a) / aspect;
- pset(x + a, y + dy, color);
- pset(x - a, y + dy, color);
- pset(x + a, y - dy, color);
- pset(x - a, y - dy, color);
- }
-
- for (b=0; b < bm; b++) {
- dx = aspect * sqrt(ys - b*b);
- pset(x + dx, y + b, color);
- pset(x + dx, y - b, color);
- pset(x - dx, y + b, color);
- pset(x - dx, y - b, color);
- }
- }
-
- /* The fellipse() routine below draws on the graphics screen in the
- * specified color an ellipse centered at coordinates (x,y) on the
- * screen and having width 2*a and height 2*b. The fcircle() routine
- * is a variation of the fellipse() function in which the width and
- * height of the ellipse are equal.
- *
- * These routines were derived from Pascal ellipse-drawing routines
- * in an article by Jerry Van Aken and Mark Novak in "ACM Transactions
- * on Graphics," April, 1985, and draw circles faster and as good as
- * or better than any other routine I have seen.
- */
-
- void fellipse(center_x, center_y, a0, b0, color)
- int center_x, center_y, a0, b0, color;
-
- {
- static long t1, t2, t3, t4, t5, t6, t7, t8, t9, d1, d2;
- static long a, b; /* Hold long representations of
- * a0, b0
- */
- register int x, y;
-
- a=a0; b=b0;
- x=a; y=0;
-
- t1=a*a; t2=t1*2; t3=t2*2;
- t4=b*b; t5=t4*2; t6=t5*2;
- t7=t5*a; t8=t7*2; t9=0;
-
- d1=t2-t7+t4/2; d2=t1/2-t8+t5;
-
- while (d2 < 0) {
- pset(center_x+x, center_y+y, color);
- pset(center_x-x, center_y+y, color);
- pset(center_x+x, center_y-y, color);
- pset(center_x-x, center_y-y, color);
-
- ++y;
- t9 += t3;
-
- if (d1 < 0) {
- d1 += t9+t2;
- d2 += t9;
- } else {
- --x;
- t8 -= t6;
- d1 += t9-t8+t2;
- d2 += t5-t8+t9;
- }
- }
-
- do {
- pset(center_x+x, center_y+y, color);
- pset(center_x-x, center_y+y, color);
- pset(center_x+x, center_y-y, color);
- pset(center_x-x, center_y-y, color);
-
- --x;
- t8 -= t6;
-
- if (d2 < 0) {
- ++y;
- t9 += t3;
- d2 += t5-t8+t9;
- } else
- d2 += t5-t8;
-
- } while (x >= 0);
- }
-
- /* The following routine draws on the screen in the specified color a circle
- * centered at the given coordinates and having the specified radius. Note
- * that it enlarges the width of the circle to compensate for the monitor's
- * aspect ratio.
- */
-
- void fcircle(center_x, center_y, radius, color)
- int center_x, center_y, radius, color;
-
- {
- fellipse(center_x, center_y, (int)(radius*aspect), radius, color);
- }
-
-
- /* D R A W A B O X
- */
-
- /* This routine draws the outline of a rectangle on the screen, two of
- * whose corners are located at (x1,y1) and (x2,y2).
- */
-
- void box(x1, y1, x2, y2, color)
- unsigned int x1, y1, x2, y2, color;
-
- {
- line(x1, y1, x2, y1, color);
- line(x2, y1, x2, y2, color);
- line(x1, y1, x1, y2, color);
- line(x1, y2, x2, y2, color);
- }
-
- /* F A S T B O X D R A W
- */
-
- /* This routine is functionally identical to the one above, but it
- * uses the fast line-drawing routine to draw the box.
- */
-
- void fbox(x1, y1, x2, y2, color)
- unsigned int x1, y1, x2, y2, color;
-
- {
- fline(x1, y1, x2, y1, color);
- fline(x2, y1, x2, y2, color);
- fline(x1, y1, x1, y2, color);
- fline(x1, y2, x2, y2, color);
- }
-
- /* D R A W A S O L I D B O X
- */
-
- /* The following routine draws a solid rectangle on the screen, two of
- * whose corners are located at (x1,y1) and (x2,y2), in the specified
- * color.
- */
-
- fillbox(x1, y1, x2, y2, color)
- unsigned int x1, y1, x2, y2, color;
-
- {
- unsigned int y;
-
- if (y1 > y2)
- SWAP(y1, y2);
-
- for (y=y1; y<=y2; y++)
- line(x1, y, x2, y, color);
- }
-
- /* The next routine is functionally identical to the one above, but
- * uses the fline() routine for faster operation.
- */
-
- ffillbox(x1, y1, x2, y2, color)
- unsigned int x1, y1, x2, y2, color;
-
- {
- unsigned int y;
-
- if (y1 > y2)
- SWAP(y1, y2);
-
- for (y=y1; y<=y2; y++)
- fline(x1, y, x2, y, color);
- }
-
- /* C L E A R S C R E E N
- */
-
- cls()
- {
- setvid(mode); /* Easy clear-screen function */
- }
-